/*
 * wpa_supplicant/hostapd / Debug prints
 * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * Alternatively, this software may be distributed under the terms of BSD
 * license.
 *
 * See README and COPYING for more details.
 */

#include "includes.h"

#include "common.h"


#ifdef CONFIG_DEBUG_FILE
static FILE *out_file = NULL;
#endif /* CONFIG_DEBUG_FILE */
int wpa_debug_level = MSG_INFO;
int wpa_debug_show_keys = 0;
int wpa_debug_timestamp = 0;


#ifndef CONFIG_NO_STDOUT_DEBUG

void wpa_debug_print_timestamp(void)
{
        struct os_time tv;

        if (!wpa_debug_timestamp)
                return;

        os_get_time(&tv);
#ifdef CONFIG_DEBUG_FILE
        if (out_file) {
                fprintf(out_file, "%ld.%06u: ", (long) tv.sec,
                        (unsigned int) tv.usec);
        } else
#endif /* CONFIG_DEBUG_FILE */
        printf("%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec);
}


/**
 * wpa_printf - conditional printf
 * @level: priority level (MSG_*) of the message
 * @fmt: printf format string, followed by optional arguments
 *
 * This function is used to print conditional debugging and error messages. The
 * output may be directed to stdout, stderr, and/or syslog based on
 * configuration.
 *
 * Note: New line '\n' is added to the end of the text when printing to stdout.
 */
void wpa_printf(int level, const char *fmt, ...)
{
        va_list ap;

        va_start(ap, fmt);
        if (level >= wpa_debug_level) {
                wpa_debug_print_timestamp();
#ifdef CONFIG_DEBUG_FILE
                if (out_file) {
                        vfprintf(out_file, fmt, ap);
                        fprintf(out_file, "\n");
                } else {
#endif /* CONFIG_DEBUG_FILE */
                vprintf(fmt, ap);
                printf("\n");
#ifdef CONFIG_DEBUG_FILE
                }
#endif /* CONFIG_DEBUG_FILE */
        }
        va_end(ap);
}


static void _wpa_hexdump(int level, const char *title, const u8 *buf,
                         size_t len, int show)
{
        size_t i;
        if (level < wpa_debug_level)
                return;
        wpa_debug_print_timestamp();
#ifdef CONFIG_DEBUG_FILE
        if (out_file) {
                fprintf(out_file, "%s - hexdump(len=%lu):",
                        title, (unsigned long) len);
                if (buf == NULL) {
                        fprintf(out_file, " [NULL]");
                } else if (show) {
                        for (i = 0; i < len; i++)
                                fprintf(out_file, " %02x", buf[i]);
                } else {
                        fprintf(out_file, " [REMOVED]");
                }
                fprintf(out_file, "\n");
        } else {
#endif /* CONFIG_DEBUG_FILE */
        printf("%s - hexdump(len=%lu):", title, (unsigned long) len);
        if (buf == NULL) {
                printf(" [NULL]");
        } else if (show) {
                for (i = 0; i < len; i++)
                        printf(" %02x", buf[i]);
        } else {
                printf(" [REMOVED]");
        }
        printf("\n");
#ifdef CONFIG_DEBUG_FILE
        }
#endif /* CONFIG_DEBUG_FILE */
}

void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len)
{
        _wpa_hexdump(level, title, buf, len, 1);
}


void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len)
{
        _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys);
}


static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf,
                               size_t len, int show)
{
        size_t i, llen;
        const u8 *pos = buf;
        const size_t line_len = 16;

        if (level < wpa_debug_level)
                return;
        wpa_debug_print_timestamp();
#ifdef CONFIG_DEBUG_FILE
        if (out_file) {
                if (!show) {
                        fprintf(out_file,
                                "%s - hexdump_ascii(len=%lu): [REMOVED]\n",
                                title, (unsigned long) len);
                        return;
                }
                if (buf == NULL) {
                        fprintf(out_file,
                                "%s - hexdump_ascii(len=%lu): [NULL]\n",
                                title, (unsigned long) len);
                        return;
                }
                fprintf(out_file, "%s - hexdump_ascii(len=%lu):\n",
                        title, (unsigned long) len);
                while (len) {
                        llen = len > line_len ? line_len : len;
                        fprintf(out_file, "    ");
                        for (i = 0; i < llen; i++)
                                fprintf(out_file, " %02x", pos[i]);
                        for (i = llen; i < line_len; i++)
                                fprintf(out_file, "   ");
                        fprintf(out_file, "   ");
                        for (i = 0; i < llen; i++) {
                                if (isprint(pos[i]))
                                        fprintf(out_file, "%c", pos[i]);
                                else
                                        fprintf(out_file, "_");
                        }
                        for (i = llen; i < line_len; i++)
                                fprintf(out_file, " ");
                        fprintf(out_file, "\n");
                        pos += llen;
                        len -= llen;
                }
        } else {
#endif /* CONFIG_DEBUG_FILE */
        if (!show) {
                printf("%s - hexdump_ascii(len=%lu): [REMOVED]\n",
                       title, (unsigned long) len);
                return;
        }
        if (buf == NULL) {
                printf("%s - hexdump_ascii(len=%lu): [NULL]\n",
                       title, (unsigned long) len);
                return;
        }
        printf("%s - hexdump_ascii(len=%lu):\n", title, (unsigned long) len);
        while (len) {
                llen = len > line_len ? line_len : len;
                printf("    ");
                for (i = 0; i < llen; i++)
                        printf(" %02x", pos[i]);
                for (i = llen; i < line_len; i++)
                        printf("   ");
                printf("   ");
                for (i = 0; i < llen; i++) {
                        if (isprint(pos[i]))
                                printf("%c", pos[i]);
                        else
                                printf("_");
                }
                for (i = llen; i < line_len; i++)
                        printf(" ");
                printf("\n");
                pos += llen;
                len -= llen;
        }
#ifdef CONFIG_DEBUG_FILE
        }
#endif /* CONFIG_DEBUG_FILE */
}


void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len)
{
        _wpa_hexdump_ascii(level, title, buf, len, 1);
}


void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf,
                           size_t len)
{
        _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
}


int wpa_debug_open_file(const char *path)
{
#ifdef CONFIG_DEBUG_FILE
        if (!path)
                return 0;
        out_file = fopen(path, "a");
        if (out_file == NULL) {
                wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open "
                           "output file, using standard output");
                return -1;
        }
#ifndef _WIN32
        setvbuf(out_file, NULL, _IOLBF, 0);
#endif /* _WIN32 */
#endif /* CONFIG_DEBUG_FILE */
        return 0;
}


void wpa_debug_close_file(void)
{
#ifdef CONFIG_DEBUG_FILE
        if (!out_file)
                return;
        fclose(out_file);
        out_file = NULL;
#endif /* CONFIG_DEBUG_FILE */
}

#endif /* CONFIG_NO_STDOUT_DEBUG */


#ifndef CONFIG_NO_WPA_MSG
static wpa_msg_cb_func wpa_msg_cb = NULL;

void wpa_msg_register_cb(wpa_msg_cb_func func)
{
        wpa_msg_cb = func;
}


void wpa_msg(void *ctx, int level, const char *fmt, ...)
{
        va_list ap;
        char *buf;
        const int buflen = 2048;
        int len;

        buf = os_malloc(buflen);
        if (buf == NULL) {
                wpa_printf(MSG_ERROR, "wpa_msg: Failed to allocate message "
                           "buffer");
                return;
        }
        va_start(ap, fmt);
        len = vsnprintf(buf, buflen, fmt, ap);
        va_end(ap);
        wpa_printf(level, "%s", buf);
        if (wpa_msg_cb)
                wpa_msg_cb(ctx, level, buf, len);
        os_free(buf);
}


void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
{
        va_list ap;
        char *buf;
        const int buflen = 2048;
        int len;

        if (!wpa_msg_cb)
                return;

        buf = os_malloc(buflen);
        if (buf == NULL) {
                wpa_printf(MSG_ERROR, "wpa_msg_ctrl: Failed to allocate "
                           "message buffer");
                return;
        }
        va_start(ap, fmt);
        len = vsnprintf(buf, buflen, fmt, ap);
        va_end(ap);
        wpa_msg_cb(ctx, level, buf, len);
        os_free(buf);
}
#endif /* CONFIG_NO_WPA_MSG */


#ifndef CONFIG_NO_HOSTAPD_LOGGER
static hostapd_logger_cb_func hostapd_logger_cb = NULL;

void hostapd_logger_register_cb(hostapd_logger_cb_func func)
{
        hostapd_logger_cb = func;
}


void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level,
                    const char *fmt, ...)
{
        va_list ap;
        char *buf;
        const int buflen = 2048;
        int len;

        buf = os_malloc(buflen);
        if (buf == NULL) {
                wpa_printf(MSG_ERROR, "hostapd_logger: Failed to allocate "
                           "message buffer");
                return;
        }
        va_start(ap, fmt);
        len = vsnprintf(buf, buflen, fmt, ap);
        va_end(ap);
        if (hostapd_logger_cb)
                hostapd_logger_cb(ctx, addr, module, level, buf, len);
        else
                wpa_printf(MSG_DEBUG, "hostapd_logger: %s", buf);
        os_free(buf);
}
#endif /* CONFIG_NO_HOSTAPD_LOGGER */
